#ifndef __FT_FIFO_H
#define __FT_FIFO_H

#include <pthread.h>

struct ft_fifo {
	unsigned char *buffer;	/* the buffer holding the data */
	unsigned int size;	/* the size of the allocated buffer */
	unsigned int in;	/* data is added at offset (in % size) */
	unsigned int out;	/* data is extracted from off. (out % size) */
	pthread_mutex_t lock;	/* protects concurrent modifications */
};

extern struct ft_fifo *ft_fifo_init(unsigned char *buffer, unsigned int size);
extern struct ft_fifo *ft_fifo_alloc(unsigned int size);
extern void ft_fifo_free(struct ft_fifo *fifo);
extern unsigned int __ft_fifo_put(struct ft_fifo *fifo,
				  unsigned char *buffer, unsigned int len);
extern unsigned int __ft_fifo_get(struct ft_fifo *fifo,
				  unsigned char *buffer, unsigned int len);

/**
 * ft_fifo_put - puts some data into the FIFO
 * @fifo: the fifo to be used.
 * @buffer: the data to be added.
 * @len: the length of the data to be added.
 *
 * This function copies at most 'len' bytes from the 'buffer' into
 * the FIFO depending on the free space, and returns the number of
 * bytes copied.
 */
static inline unsigned int ft_fifo_put(struct ft_fifo *fifo,
				       unsigned char *buffer, unsigned int len)
{
	unsigned int ret;

	pthread_mutex_lock(&fifo->lock);

	ret = __ft_fifo_put(fifo, buffer, len);

	pthread_mutex_unlock(&fifo->lock);

	return ret;
}

/**
 * ft_fifo_get - gets some data from the FIFO
 * @fifo: the fifo to be used.
 * @buffer: where the data must be copied.
 * @len: the size of the destination buffer.
 *
 * This function copies at most 'len' bytes from the FIFO into the
 * 'buffer' and returns the number of copied bytes.
 */
static inline unsigned int ft_fifo_get(struct ft_fifo *fifo,
				       unsigned char *buffer, unsigned int len)
{
	unsigned int ret;

	pthread_mutex_lock(&fifo->lock);

	ret = __ft_fifo_get(fifo, buffer, len);

	/*
	 * optimization: if the FIFO is empty, set the indices to 0
	 * so we don't wrap the next time
	 */
	if (fifo->in == fifo->out)
		fifo->in = fifo->out = 0;

	pthread_mutex_unlock(&fifo->lock);

	return ret;
}

/**
 * __ft_fifo_len - returns the number of bytes available in the FIFO, no locking version
 * @fifo: the fifo to be used.
 */
static inline unsigned int __ft_fifo_len(struct ft_fifo *fifo)
{
	return fifo->in - fifo->out;
}

/**
 * ft_fifo_len - returns the number of bytes available in the FIFO
 * @fifo: the fifo to be used.
 */
static inline unsigned int ft_fifo_len(struct ft_fifo *fifo)
{
	unsigned int ret;

	pthread_mutex_lock(&fifo->lock);

	ret = __ft_fifo_len(fifo);

	pthread_mutex_unlock(&fifo->lock);

	return ret;
}

#endif /* __FT_FIFO_H */
